<?php
// RSA加解密/签名/验证签名
// http://docs.ijingyi.com/web/#/36/2131

namespace JyUtils\Rsa;

class Rsa
{
  private $public_key;
  private $private_key;
  private $private_key_passphrase;
  
  public function __construct($public_key, $private_key, $private_key_passphrase = '')
  {
    if (file_exists($public_key)) {
      if (strpos($public_key, 'file://') === false) {
        $public_key = 'file://' . $public_key;
      }
    }
    if (file_exists($private_key)) {
      if (strpos($private_key, 'file://') === false) {
        $private_key = 'file://' . $private_key;
      }
    }
    $this->public_key             = $public_key;
    $this->private_key            = $private_key;
    $this->private_key_passphrase = $private_key_passphrase;
  }
  
  /**
   * 签名
   *
   * @param string $data         待签名数据
   * @param bool   $returnBase64 可空，默认true，返回的签名是否base64编码
   * @return mixed|string 返回签名
   */
  public function sign($data, $returnBase64 = true)
  {
    $pkeyid = openssl_pkey_get_private($this->private_key, $this->private_key_passphrase);
    // compute signature
    openssl_sign(openssl_digest($data, 'sha512'), $signature, $pkeyid);
    // free the key from memory
    openssl_free_key($pkeyid);
    // return $signature;
    return $returnBase64 ? base64_encode($signature) : $signature;
  }
  
  /**
   * 验证签名
   *
   * @param string $data       原文数据
   * @param string $signature  签名
   * @param bool   $base64Data 可空，默认true，签名是否base64解码
   * @return bool
   */
  public function verify($data, $signature, $base64Data = true)
  {
    if ($base64Data) {
      $signature = base64_decode($signature);
    }
    $pubkeyid = openssl_pkey_get_public($this->public_key);
    $result   = openssl_verify(openssl_digest($data, 'sha512'), $signature, $pubkeyid);
    // free the key from memory
    openssl_free_key($pubkeyid);
    return $result == 1;
  }
  
  /**
   * 加密
   *
   * @param string $data         待加密原文
   * @param bool   $returnBase64 是否返回base64数据
   * @return false|string
   */
  public function encrypt($data, $returnBase64 = true)
  {
    $encrypted = '';
    $data      = str_split($data, 100);
    $pkeyid    = openssl_pkey_get_private($this->private_key, $this->private_key_passphrase);
    // dd('aa', $pkeyid);
    foreach ($data as $chunk) {
      // using for example OPENSSL_PKCS1_PADDING as padding
      $encryptionOk = openssl_private_encrypt($chunk, $partialEncrypted, $pkeyid, OPENSSL_PKCS1_PADDING);
      if ($encryptionOk === false) return false; // also you can return and error. If too big this will be false
      $encrypted .= $partialEncrypted;
    }
    openssl_free_key($pkeyid);
    return $returnBase64 ? base64_encode($encrypted) : $encrypted; // encoding the whole binary String as MIME base 64
  }
  
  /**
   * 解密
   *
   * @param string $data       待解密数据
   * @param bool   $base64Data 待解密数据是否为base64编码
   * @return false|string
   */
  public function decrypt($data, $base64Data = true)
  {
    if ($base64Data) {
      $data = base64_decode($data);
    }
    $decrypted = '';
    $pubkeyid  = openssl_pkey_get_public($this->public_key);
    // decode must be done before spliting for getting the binary String
    $data = str_split($data, 128);
    foreach ($data as $chunk) {
      // be sure to match padding
      $decryptionOK = openssl_public_decrypt($chunk, $partial, $pubkeyid, OPENSSL_PKCS1_PADDING);
      if ($decryptionOK === false) return false; // here also processed errors in decryption. If too big this will be false
      $decrypted .= $partial;
    }
    openssl_free_key($pubkeyid);
    return $decrypted;
  }
}
